<template>
  <el-card class="object-editor">
    <template #header>
      <div class="object-editor__header">
        <span class="object-editor__title">{{ title || '对象编辑器' }}</span>
        <el-button v-if="allowAddProperty" @click="addProperty" type="primary">
          添加属性
        </el-button>
      </div>
    </template>

    <div class="object-editor__content">
      <ObjectProperty
        v-for="(value, key) in modelValue"
        :key="key"
        :property-key="key"
        :value="value"
        :path="[key]"
        :allow-delete="allowDeleteProperty"
        :allow-add-property="allowAddProperty"
        :max-depth="maxDepth"
        :current-depth="1"
        @update:value="updateProperty"
        @delete="deleteProperty"
        @rename-key="renameProperty"
      />

      <el-empty
        v-if="Object.keys(modelValue || {}).length === 0"
        description="暂无属性"
        :image-size="80"
      >
        <el-button v-if="allowAddProperty" @click="addProperty" type="primary">
          添加第一个属性
        </el-button>
      </el-empty>
    </div>
  </el-card>
</template>

<script>
import { defineComponent } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import ObjectProperty from './ObjectProperty.vue';

export default defineComponent({
  name: 'ObjectEditor',
  components: {
    ObjectProperty,
  },
  props: {
    modelValue: {
      type: Object,
      default: () => ({}),
    },
    title: {
      type: String,
      default: '',
    },
    allowAddProperty: {
      type: Boolean,
      default: true,
    },
    allowDeleteProperty: {
      type: Boolean,
      default: true,
    },
    maxDepth: {
      type: Number,
      default: 10,
    },
  },
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    // 辅助函数：深拷贝对象或数组
    const deepClone = (obj) => {
      if (obj === null || typeof obj !== 'object') {
        return obj;
      }
      if (Array.isArray(obj)) {
        return obj.map((item) => deepClone(item));
      }
      const cloned = {};
      for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          cloned[key] = deepClone(obj[key]);
        }
      }
      return cloned;
    };

    const addProperty = async () => {
      try {
        const { value: newKey } = await ElMessageBox.prompt(
          '请输入属性名',
          '添加属性',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            inputPattern: /\S+/,
            inputErrorMessage: '属性名不能为空',
          },
        );

        const key = newKey.trim();
        if (props.modelValue.hasOwnProperty(key)) {
          ElMessage.warning('属性名已存在');
          return;
        }

        const newValue = { ...props.modelValue };
        newValue[key] = '';
        emit('update:modelValue', newValue);
      } catch {
        // 用户取消操作
      }
    };

    const updateProperty = (path, value) => {
      const newValue = deepClone(props.modelValue);
      let current = newValue;

      // 导航到目标位置
      for (let i = 0; i < path.length - 1; i++) {
        current = current[path[i]];
      }

      // 更新值
      current[path[path.length - 1]] = value;
      emit('update:modelValue', newValue);
    };

    const deleteProperty = (path) => {
      const newValue = deepClone(props.modelValue);
      let current = newValue;

      // 导航到父级对象
      for (let i = 0; i < path.length - 1; i++) {
        current = current[path[i]];
      }

      const lastKey = path[path.length - 1];

      if (Array.isArray(current) && typeof lastKey === 'number') {
        // 如果是数组，使用 splice 删除元素
        current.splice(lastKey, 1);
      } else {
        // 如果是对象，使用 delete 操作符删除属性
        delete current[lastKey];
      }
      emit('update:modelValue', newValue);
    };

    const renameProperty = async (path, oldKey, newKey) => {
      const newModelValue = deepClone(props.modelValue);
      let current = newModelValue;
      let parent = null;
      let parentKey = null;

      // 导航到父级对象
      for (let i = 0; i < path.length - 1; i++) {
        parent = current;
        parentKey = path[i];
        current = current[path[i]];
      }

      // 检查旧键名是否存在
      if (!current.hasOwnProperty(oldKey)) {
        ElMessage.warning(`属性 '${oldKey}' 不存在`);
        return;
      }

      // 检查新键名是否已存在且不是旧键名本身
      if (current.hasOwnProperty(newKey) && newKey !== oldKey) {
        ElMessage.warning(`属性名 '${newKey}' 已存在`);
        return;
      }

      // 创建一个新对象来存储修改后的属性，以保持顺序
      const updatedObject = {};
      for (const key in current) {
        if (Object.prototype.hasOwnProperty.call(current, key)) {
          if (key === oldKey) {
            updatedObject[newKey] = current[key];
          } else {
            updatedObject[key] = current[key];
          }
        }
      }

      // 将修改后的对象赋值回其父级或根级
      if (parent) {
        parent[parentKey] = updatedObject;
      } else {
        // 如果是顶级属性，直接替换 newModelValue 的内容
        // 先清空 newModelValue 的所有属性
        Object.keys(newModelValue).forEach((key) => delete newModelValue[key]);
        // 再复制新属性
        Object.assign(newModelValue, updatedObject);
      }

      emit('update:modelValue', newModelValue);
    };

    return {
      addProperty,
      updateProperty,
      deleteProperty,
      renameProperty,
    };
  },
});
</script>

<style scoped>
.object-editor {
  width: 100%;
}

.object-editor__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.object-editor__title {
  font-size: 16px;
  font-weight: 600;
}

.object-editor__content {
  min-height: 100px;
}
</style>
